package com.lds.register.center.server.business.appinstance.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lds.management.center.common.model.dto.ResultDTO;
import com.lds.management.center.common.util.AssertUtil;
import com.lds.register.center.client.common.model.request.HeartbeatRequest;
import com.lds.register.center.client.common.model.request.RegisterAppInstanceRequest;
import com.lds.register.center.client.common.model.response.RegisterAppInstanceResponse;
import com.lds.register.center.server.business.app.entity.App;
import com.lds.register.center.server.business.app.mapper.AppMapper;
import com.lds.register.center.server.business.appinstance.entity.AppInstance;
import com.lds.register.center.server.business.appinstance.mapper.AppInstanceMapper;
import com.lds.register.center.server.business.appinstance.service.AppInstanceRemoteService;
import com.lds.register.center.server.business.cluster.mapper.ClusterMapper;
import com.lds.register.center.server.business.env.mapper.EnvMapper;
import com.lds.register.center.server.common.enums.ServerErrorEnum;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Service
@RequiredArgsConstructor
public class AppInstanceRemoteServiceImpl implements AppInstanceRemoteService {
    private final EnvMapper envMapper;
    private final ClusterMapper clusterMapper;
    private final AppMapper appMapper;
    private final AppInstanceMapper appInstanceMapper;
    /**
     * 缓存心跳
     * key: appInstanceId
     * value: lastHeartbeatTime
     */
    private final Map<Long, Long> heartbeatMap = new ConcurrentHashMap<>();

    @Override
    public ResultDTO<RegisterAppInstanceResponse> registerAppInstance(RegisterAppInstanceRequest request) {
        Long envId = envMapper.getEnvIdByName(request.getEnvName());
        AssertUtil.assertTrue(Objects.nonNull(envId), ServerErrorEnum.ENV_NOT_EXIST);

        Long clusterId = clusterMapper.getClusterIdByEnvIdAndName(envId, request.getClusterName());
        AssertUtil.assertTrue(Objects.nonNull(clusterId), ServerErrorEnum.CLUSTER_NOT_EXIST);

        Long appId = getAppId(clusterId, request.getAppName(), request.isEphemeral());
        Long appInstanceId = saveOrUpdateAppInstance(appId, request.getAppInstanceIp(), request.getAppInstancePort(), request.isEphemeral());
        return ResultDTO.success(new RegisterAppInstanceResponse(appId, appInstanceId));
    }

    private Long getAppId(Long clusterId, String appName, boolean ephemeral) {
        Long appId = appMapper.getAppIdByClusterIdAndName(clusterId, appName);
        if (Objects.nonNull(appId)) return appId;

        LocalDateTime now = LocalDateTime.now();
        App app = App.builder()
                .clusterId(clusterId)
                .name(appName)
                .creator("admin")
                .modifier("admin")
                .createTime(now)
                .updateTime(now)
                .ephemeral(ephemeral)
                .build();
        appMapper.insert(app);
        return app.getId();
    }

    private Long saveOrUpdateAppInstance(Long appId, String appInstanceIp, int appInstancePort, boolean ephemeral) {
        AppInstance currAppInstance = appInstanceMapper.selectOne(new QueryWrapper<>(AppInstance.builder().appId(appId).ip(appInstanceIp).port(appInstancePort).build()));
        if (Objects.isNull(currAppInstance)) {
            LocalDateTime now = LocalDateTime.now();
            AppInstance appInstance = AppInstance.builder()
                    .appId(appId)
                    .ip(appInstanceIp)
                    .port(appInstancePort)
                    .healthy(true)
                    .weight(1)
                    .enabled(true)
                    .creator("admin")
                    .modifier("admin")
                    .createTime(now)
                    .updateTime(now)
                    .build();
            appInstanceMapper.insert(appInstance);
            return appInstance.getId();
        } else {
            if (BooleanUtils.isFalse(currAppInstance.getHealthy())) {
                appInstanceMapper.updateById(
                        AppInstance.builder()
                                .id(currAppInstance.getId())
                                .healthy(true)
                                .updateTime(LocalDateTime.now())
                                .build());
            }
            return currAppInstance.getId();
        }
    }

    @Override
    public ResultDTO<Boolean> heartbeat(HeartbeatRequest request) {
        heartbeatMap.put(request.getAppInstanceId(), System.currentTimeMillis());
        return ResultDTO.success(true);
    }

    @Override
    public Map<Long, Long> getHeartbeatMap() {
        return heartbeatMap;
    }
}
